// Functions associated with the Toolset class
//-----------------

//-----------------
// Includes libraries and other files
#include "Toolset.h"
//-----------------

//-----------------
//// CONSTRUCTOR - Toolset
Toolset::Toolset( LPDIRECT3DDEVICE9 device, const Window& window, UserInterface* p_ui, std::vector<Mesh>* models)
	: device_( device), p_ui_( p_ui), p_models_( models), fm_( device), menubar_( window.get_screen_width()), macrobar_( window.get_screen_width(), menubar_.get_height()),
	  sidebar_assets_( window, p_ui, macrobar_.get_y()+macrobar_.get_height(), LEFT), sidebar_placed_( window, p_ui, macrobar_.get_y()+macrobar_.get_height(), RIGHT),
	  grid_size_x_( 0), grid_size_z_( 0), grid_active_x_( 0), grid_active_z_( 0), distance_( 300), models_loaded_( false), asset_border_( false), placed_border_( false)
{}

//// DESTRUCTOR - ~Toolset
Toolset::~Toolset() {
	fm_.export_assets( sidebar_assets_.assets_, audio_assets_, ignored_assets_);

	device_ = nullptr;
	p_ui_ = nullptr;
}
//-----------------

//-----------------
//// FUNCTION - get_grid_size_x
// return the size of the grid in the x direction
int Toolset::get_grid_size_x() const {
	// Pre-conditions :- none
	assert( true);

	return grid_size_x_;
}

//// FUNCTION - get_grid_size_z
// return the size of the grid in the z direction
int Toolset::get_grid_size_z() const {
	// Pre-conditions :- none
	assert( true);

	return grid_size_z_;
}

//// FUNCTION - get_active_grid_square
// return the x and z coordinates of the currently selected grid block
void Toolset::get_active_grid_square( int& x, int& z) const {
	// Pre-conditions :- none
	assert( true);

	x = grid_active_x_;
	z = grid_active_z_;
}

//// FUNCTION - load_assets
// load in all the model and audio files
HRESULT Toolset::load_assets() {
	// Pre-conditions :- none
	assert( true);

	HRESULT hr;

	// setup the geometry and textures for the various toolbars
	hr =	  menubar_.setup_geometry(			device_);
	hr = hr | macrobar_.setup_geometry(			device_);
	hr = hr | sidebar_assets_.setup_geometry(	device_);
	hr = hr | sidebar_placed_.setup_geometry(	device_);
	hr = hr | toolset_tile_.load_mesh(			device_, "object_toolset_tile.X");
	hr = hr | selected_tile_.setup(				device_);
	hr = hr | D3DXCreateTextureFromFile(		device_, (PATH_ORTHOGRAPHIC+"texture_toolset_bg_light.png").c_str(), &texture_highlight_);

	// load in all the model and audio files that were recognised and saved when the program closed last time
	fm_.extract_previous_assets( p_models_, sidebar_assets_.assets_, audio_assets_, ignored_assets_);

	{
		DIR *dir;
		struct dirent *ent;

		// load in all the model files
		dir = opendir( PATH_MODELS.c_str());
		if( dir != nullptr) {
			// run through all the files within directory
			while(( ent = readdir( dir)) != nullptr)
				if( ent->d_type == DT_REG) {
					ModelAsset temp;

					if( fm_.is_valid_format( ent->d_name, "model")) {				// If the file is a valid format...
						Mesh m;
						if( SUCCEEDED( m.load_mesh( device_, ent->d_name))) {		// ... and the mesh loads...
							p_models_->push_back( m);								// ... then add it to the model vector...
							
							temp.set_mesh( &p_models_->at( p_models_->size()-1));	// ... and set the last item in the vector as the asset's mesh...
							sidebar_assets_.assets_.push_back( temp);				// ... and then add the object to the sidebar's asset list
						}
					}
				}
				closedir( dir);
		}
		else
			// could not open directory
			MessageBox( NULL, "Error Code: 005", "Toolset::load_assets", MB_OK);

		// load in all the audio files
		dir = opendir( PATH_AUDIO.c_str());
		if( dir != nullptr) {
			// run through all the files within directory
			while(( ent = readdir( dir)) != nullptr)
				if( ent->d_type == DT_REG) {
					AudioAsset temp( ent->d_name);

					if( fm_.is_valid_format( ent->d_name, "audio"))		// If the file is a valid format...
						audio_assets_.push_back( temp);					// ... then add the file to the audio list
				}
				closedir( dir);
		}
		else
			// could not open directory
			MessageBox( NULL, "Error Code: 005", "Toolset::load_assets", MB_OK);
	}

	// sort the lists and remove any duplicates
	sidebar_assets_.assets_.sort();
	sidebar_assets_.assets_.unique();
	audio_assets_.sort();
	audio_assets_.unique();

	// remove any items from the lists which, according to the external file assets.is, shouldn't be there
	for( std::list<std::string>::iterator i( ignored_assets_.begin()); i != ignored_assets_.end();) {
		bool item_removed( false);

		for( std::list<ModelAsset>::iterator a( sidebar_assets_.assets_.begin()); a != sidebar_assets_.assets_.end(); ++a) {
			// if the list contains the file name...
			if( a->get_file_name() == *i) {
				// ... then delete the item from the list, set the boolean to review the list again and break the inner loop
				sidebar_assets_.assets_.remove( *a);
				item_removed = true;
				break;
			}
		}

		// if no item was removed then move on
		if( !item_removed)
			++i;
	}

	for( std::list<std::string>::iterator i( ignored_assets_.begin()); i != ignored_assets_.end();) {
		bool item_removed( false);

		for( std::list<AudioAsset>::iterator a( audio_assets_.begin()); a != audio_assets_.end(); ++a) {
			// if the list contains the file name...
			if( a->get_file_name() == *i) {
				// ... then delete the item from the list, set the boolean to review the list again and break the inner loop
				audio_assets_.remove( *a);
				item_removed = true;
				break;
			}
		}

		// if no item was removed then move on
		if( !item_removed)
			++i;
	}
	
	// ##### NOTE #####
	// Need a dialogue box to pop up to enter the actual name of any assets which don't have a proper name.
	// For now, the following loop has been implemented to give the assets a name.
	for( std::list<ModelAsset>::iterator i( sidebar_assets_.assets_.begin()); i != sidebar_assets_.assets_.end(); ++i)
		if( i->get_asset_name() == "")
			i->fill_in_the_blanks( sidebar_assets_.BUTTON_MISC);

	for( std::list<AudioAsset>::iterator i( audio_assets_.begin()); i != audio_assets_.end(); ++i)
		if( i->get_asset_name() == "")
			i->fill_in_the_blanks();
	// ##### NOTE #####

	models_loaded_ = true;

	return hr;
}

//// FUNCTION - set_grid_size
// sets the size of the toolset's grid
void Toolset::set_grid_size( const int& x, const int& z) {
	// Pre-conditions :- none
	assert( true);

	grid_size_x_ = x;
	grid_size_z_ = z;

	// set the size of the dynamic 2D array (vectors) that will hold the placed objects
	sidebar_placed_.placed_floor_.resize( x);
	sidebar_placed_.placed_objects_.resize( x);
	for( int i( 0); i < x; ++i) {
		sidebar_placed_.placed_floor_[i].resize( z);
		sidebar_placed_.placed_objects_[i].resize( z);
	}
}

//// FUNCTION - move_grid_selection_up
// move the grid selector up, if possible
void Toolset::move_grid_selection_up() {
	// Pre-conditions :- none
	assert( true);

	// if the selection is not currently at the top of the grid, then move up
	if( grid_active_x_ != (grid_size_x_-1)*GRID_SIZE)
		grid_active_x_ += GRID_SIZE;
}

//// FUNCTION - move_grid_selection_down
// move the grid selector down, if possible
void Toolset::move_grid_selection_down() {
	// Pre-conditions :- none
	assert( true);

	// if the selection is not currently at the bottom of the grid, then move down
	if( grid_active_x_ != 0)
		grid_active_x_ -= GRID_SIZE;
}

//// FUNCTION - move_grid_selection_left
// move the grid selector left, if possible
void Toolset::move_grid_selection_left() {
	// Pre-conditions :- none
	assert( true);

	// if the selection is not currently at the left-most of the grid, then move left
	if( grid_active_z_ != (grid_size_z_-1)*GRID_SIZE)
		grid_active_z_ += GRID_SIZE;
}

//// FUNCTION - move_grid_selection_right
// move the grid selector right, if possible
void Toolset::move_grid_selection_right() {
	// Pre-conditions :- none
	assert( true);

	// if the selection is not currently at the right-most of the grid, then move right
	if( grid_active_z_ != 0)
		grid_active_z_ -= GRID_SIZE;
}

//// FUNCTION - delete_grid_selection
// delete the object(s) in the currently selected grid block
void Toolset::delete_grid_selection() {
	// Pre-conditions :- none
	assert( true);

	// create a blank variable and set it to where the current object is
	PlacedObject temp;
	sidebar_placed_.placed_floor_[grid_active_x_/GRID_SIZE][grid_active_z_/GRID_SIZE] = temp;
	sidebar_placed_.placed_objects_[grid_active_x_/GRID_SIZE][grid_active_z_/GRID_SIZE] = temp;
}

//// FUNCTION - process_interaction
// test for any manipulation of variables based on current input states
void Toolset::process_interaction( const Window& window, Cursor& mouse) {
	// Pre-conditions :- none
	assert( true);

	// make the mouse visible as it's always shown during the toolset
	mouse.set_visible( true);

	{
		int x( mouse.get_x()), y( window.invert_y( mouse.get_y()));

		// if the mouse is over a button then set it to active...
		if( menubar_.mouseover_button(			x, y) ||
			macrobar_.mouseover_button(			x, y) ||
			sidebar_assets_.mouseover_button(	x, y) ||
			sidebar_placed_.mouseover_button(	x, y))
			mouse.set_active( true);
		// ... or if the mouse is over an asset then set it to active
		else if( sidebar_assets_.mouseover_asset( x, y) ||
			sidebar_placed_.mouseover_asset( x, y))
			mouse.set_active( true);
		// ... otherwise set the mouse to inactive
		else
			mouse.set_active( false);
	}
}

//// FUNCTION - process_input_toolset
// process the input for the toolset screen
void Toolset::process_input_toolset( const UserInput& input, Player& player, int& state, const int& STATE_MENU, const int& x, const int& y) {
	// Pre-conditions :- none
	assert( true);

	// modify the distance from the center of the grid by the movement of the mouse wheel
	distance_ -= input.get_mouse_wheel_movement()/10;

	// if the left control key and the left mouse button are pressed simultaneously...
	if( input.key_is_held( DIK_LCONTROL) && input.button_is_held( DIMOUSE_LEFTBUTTON)) {	// moving the camera
		// ... and the mouse is hovering over the active scene...
		if( !sidebar_assets_.mouseover_toolbar( x, y) &&
			!sidebar_placed_.mouseover_toolbar( x, y) &&
			!menubar_.mouseover_toolbar(		x, y) &&
			!macrobar_.mouseover_toolbar(		x, y)) {
				// ... modify the phi and theta angles based on how much mouse movement there is
				float mouseX( 0.0), mouseY( 0.0);
				input.get_mouse_movement( mouseX, mouseY);

				if( mouseX != 0) {
					// rotate the camera by modifying the lookat based on the movement of the mouse
					if( mouseX < 0)
						player.set_phi( player.get_phi() - mouseX/distance_);
					else if( mouseX > 0)
						player.set_phi( player.get_phi() - mouseX/distance_);
				}

				if( mouseY != 0) {
					// move the camera up/down based on the movement of the mouse
					if( mouseY > 0)
						player.set_theta( player.get_theta() + mouseY/distance_ * player.get_mouse_invert());
					else if (mouseY < 0)
						player.set_theta( player.get_theta() + mouseY/distance_ * player.get_mouse_invert());

					// prevents the camera from looking too far up or down
					if( player.get_theta() > D3DX_PI/2-0.0001f * player.get_mouse_invert())
						player.set_theta( D3DX_PI/2-0.0001f * player.get_mouse_invert());
					else if( player.get_theta() < -D3DX_PI/2+0.0001f * player.get_mouse_invert())
						player.set_theta( -D3DX_PI/2+0.0001f * player.get_mouse_invert());
				}
		}
	}
	// else if the left mouse button has been pressed without Ctrl...
	else if( input.button_is_pressed( DIMOUSE_LEFTBUTTON) || input.button_is_held( DIMOUSE_LEFTBUTTON)) {
		bool pressed( input.button_is_pressed( DIMOUSE_LEFTBUTTON)), held( input.button_is_held( DIMOUSE_LEFTBUTTON));

		// ... first check if it's hovering over a border...
		if( pressed && sidebar_assets_.mouseover_border( x, y))
			// ... if it's hovering over the assets border then set the variable to true
			asset_border_ = true;
		else if( pressed && sidebar_placed_.mouseover_border( x, y))
			// ... if it's hovering over the assets border then set the variable to true
			placed_border_ = true;

		// ... check to see if the mouse is held down...
		if( held) {
			// ... and if the 'clicked border' variable is true...
			if( asset_border_) {
				// ... if so, then extend the border in the direction the mouse is moving
				float mouseX( 0.0), mouseY( 0.0);
				input.get_mouse_movement( mouseX, mouseY);

				sidebar_assets_.set_width( sidebar_assets_.get_width() + mouseX);
			}
			// ... and if the 'clicked border' variable is true...
			else if( placed_border_) {
				// ... if so, then extend the border in the direction the mouse is moving
				float mouseX( 0.0), mouseY( 0.0);
				input.get_mouse_movement( mouseX, mouseY);

				sidebar_placed_.set_width( sidebar_placed_.get_width() - mouseX);
			}
		}

		// ... then check to see if the mouse if hovering over a toolbar...
		if( sidebar_assets_.mouseover_toolbar( x, y)) {
			// ... if it's over an asset then add it to the map.
			if( sidebar_assets_.mouseover_asset( x, y))
				sidebar_placed_.add_asset( grid_active_x_/GRID_SIZE, grid_active_z_/GRID_SIZE, sidebar_assets_.get_active_asset());
			// ... if it's over a button then activate that button.
			else if( sidebar_assets_.mouseover_button( x, y))
				sidebar_assets_.set_active_button();
		}
		else if( sidebar_placed_.mouseover_toolbar( x, y)) {
			// ... if it's over a button then activate that button.
			if( sidebar_placed_.mouseover_button( x, y))
				sidebar_placed_.set_active_button();
		}
		else if( menubar_.mouseover_toolbar( x, y)) {
			// ... if it's over a button then perform the appropriate action for the button.
			if( menubar_.mouseover_button( x, y)) {
				switch( menubar_.get_button()) {
					case menubar_.BUTTON_NEW:		// start a new module (i.e. delete everything)
						reset();
						break;
					case menubar_.BUTTON_LOAD:		// load a pre-existing module
						reset();
						fm_.import_module_to_toolset( p_models_, sidebar_placed_.placed_floor_, sidebar_placed_.placed_objects_, audio_assets_, sidebar_placed_.BUTTON_FLOOR, FILE_MODULE_TEST);
						break;
					case menubar_.BUTTON_SAVE:		// save the current module to an external file
						fm_.export_module( sidebar_placed_.placed_floor_, sidebar_placed_.placed_objects_, audio_assets_, FILE_MODULE_TEST);
						break;
					case menubar_.BUTTON_QUIT:		// return to the main menu
						reset();	// would be easier/more efficient if the toolset just went out of scope, but I've not really structured it in that way
						state = STATE_MENU;
						break;
				}
			}
		}
		else if( macrobar_.mouseover_toolbar( x, y)) {
			// ... nothing of any impact has currently been implemented in the macrobar yet.
		}
		else if( pressed)
			// ... if it's not over a toolbar, then rotate the object on the currently selected tile
			sidebar_placed_.placed_objects_[grid_active_x_/GRID_SIZE][grid_active_z_/GRID_SIZE].rotate_clockwise();
	}
	// else if Ctrl has been pressed without the left mouse button...
	else if( input.key_is_pressed( DIK_LCONTROL)) {
		// The left Ctrl shouldn't do anything when pressed on its own, but just in case
		// it should do something later in development this block has been created to indicate
		// that it needs to be an additional 'else if' otherwise it'll always hit even when
		// the user is rotating the view.
	}

	// if the mouse isn't being held down the the 'click border' variables shouldn't be true
	if( !input.button_is_held( DIMOUSE_LEFTBUTTON)) {
		asset_border_ = false;
		placed_border_ = false;
	}

	// if the right mouse button has been pressed...
	if( input.button_is_pressed( DIMOUSE_RIGHTBUTTON)) {
		// ... then check to see if the mouse if hovering over a toolbar...
		if( !sidebar_assets_.mouseover_toolbar( x, y) &&
			!sidebar_placed_.mouseover_toolbar( x, y) &&
			!menubar_.mouseover_toolbar(		x, y) &&
			!macrobar_.mouseover_toolbar(		x, y))
			// ... if it's not over a toolbar, then rotate the object on the currently selected tile
			sidebar_placed_.placed_floor_[grid_active_x_/GRID_SIZE][grid_active_z_/GRID_SIZE].rotate_clockwise();
	}

	// move the grid selector
	if( input.key_is_pressed( DIK_UP))			// move up
		move_grid_selection_up();
	else if( input.key_is_pressed( DIK_DOWN))	// move down
		move_grid_selection_down();
	else if( input.key_is_pressed( DIK_LEFT))	// move left
		move_grid_selection_left();
	else if( input.key_is_pressed( DIK_RIGHT))	// move right
		move_grid_selection_right();

	// delete the currently selected object
	if( input.key_is_pressed( DIK_DELETE))
		delete_grid_selection();

	// return to the main menu if escape is pressed
	if( input.key_is_pressed( DIK_ESCAPE))
		state = STATE_MENU;

	// work out the new location of the camera based on the distance from the center of the grid and the new rotation angles
	player.set_location( (cos( player.get_phi()) * cos( player.get_theta()))*distance_,
						 (sin( player.get_theta()))*distance_,
						 (sin( player.get_phi()) * cos( player.get_theta()))*distance_);
}

//// FUNCTION - render_perspective
// output the perspective portions of the toolset (i.e. the main window)
void Toolset::render_perspective() const {
	// Pre-conditions :- the device must exist and the models must've been loaded successfully
	assert( device_ != nullptr && models_loaded_);

	// render the grid
	for( int x( 0); x < grid_size_x_*GRID_SIZE; x+=GRID_SIZE)
		for( int z( 0); z < grid_size_z_*GRID_SIZE; z+=GRID_SIZE)
			toolset_tile_.render( x, 0.0, z);

	// render the plane that indicates which grid block is currently selected
	// for some unknown reason the selected_tile_ needs to be slightly offset by awkward amounts
	selected_tile_.render( texture_highlight_, grid_active_x_+(float(GRID_SIZE)/1.5), 2.0, grid_active_z_+(float(GRID_SIZE)/2), GRID_SIZE, 1.0, GRID_SIZE, 90.0, 0.0, 0.0, 100);

	// render the map as it currently stands
	for( int i( 0); i < grid_size_x_; ++i)
		for( int j( 0); j < grid_size_z_; ++j) {
			sidebar_placed_.placed_floor_[i][j].render();
			sidebar_placed_.placed_objects_[i][j].render();
		}
}

//// FUNCTION - render_orthographic
// output the orthographic portions of the toolset (i.e. the toolbars and cursor)
void Toolset::render_orthographic( const Window& window, const Cursor& mouse) {
	// Pre-conditions :- the device must exist and the models must've been loaded successfully
	assert( device_ != nullptr && models_loaded_);

	menubar_.render(		device_, window);
	macrobar_.render(		device_, window);
	sidebar_assets_.render(	device_, window);
	sidebar_placed_.render(	device_, window);
}

//// FUNCTION - reset
// clear all the user-created data in preparation of a new module
void Toolset::reset() {
	// Pre-conditions :- none
	assert( true);

	grid_active_x_ = 0;
	grid_active_z_ = 0;

	menubar_.reset();
	macrobar_.reset();
	sidebar_assets_.reset();
	sidebar_placed_.reset();
}